package com.dingwen.treasure.mongodb.service;

import cn.hutool.core.convert.Convert;
import com.dingwen.treasure.base.util.ReflectUtil;
import com.dingwen.treasure.mongodb.vo.MongoPageVo;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * dao 基类 对MongoTemplate的封装
 *
 * @author dingwen
 * 2021.06.23 14:18
 */
public class MongoBaseService<T extends Serializable> {

    /**
     * mongo 模板
     */
    @Resource
    private MongoTemplate mongoTemplate;

    /**
     * 泛型字节码
     */
    private Class<T> tClass;


    /**
     * FIELD_ID_NAME
     */
    public static final String FIELD_ID_NAME = "_id";

    /**
     * 初始化
     */
    @PostConstruct
    public void init() {
        this.tClass = ReflectUtil.getClassForGeneric(0, this);
    }


    /*
     ******************************** 查询 *************************************************
     */

    /**
     * 查询所有
     *
     * @return {@link List<T>}
     */
    public List<T> findAll() {
        return mongoTemplate.findAll(tClass);
    }

    /**
     * 根据ID集合查询列表
     *
     * @param ids id
     * @return {@link List<T>}
     */
    public List<T> findByIds(List<Serializable> ids) {
        return findList(new Query().addCriteria(Criteria.where(FIELD_ID_NAME).in(ids)));
    }

    /**
     * 根据条件查询列表
     *
     * @param query 查询
     * @return {@link List<T>}
     */
    public List<T> findList(Query query) {
        return mongoTemplate.find(query, tClass);
    }


    /**
     * 根据条件查询单个
     *
     * @param query 查询
     * @return {@link T}
     */
    public T findOne(Query query) {
        return mongoTemplate.findOne(query, tClass);
    }

    /**
     * 根据列名和值查询当个文档
     *
     * @param filed 属性名称
     * @param value 价值
     * @return {@link T}
     */
    public T findOne(String filed, String value) {
        return mongoTemplate.findOne(new Query().addCriteria(Criteria.where(filed).is(value)), tClass);
    }

    /**
     * 根据ID查询
     *
     * @param id id
     * @return {@link T}
     */
    public T findById(Serializable id) {
        return findOne(FIELD_ID_NAME, Convert.toStr(id));
    }


    /**
     * 分页查询
     *
     * @param query    查询
     * @param sort     排序
     * @param pageSize 页面大小
     * @param pageNum  页面num
     * @return {@link PageImpl<T>}
     */
    public MongoPageVo<T> findPage(Sort sort, Integer pageSize, Integer pageNum, Query query) {

        // pageSize 默认值10
        Optional.ofNullable(pageSize).orElse(10);
        // pageNum 默认值1
        Optional.ofNullable(pageNum).orElse(1);

        // 总记录条数
        long totalCount = mongoTemplate.count(query, tClass);

        // 分页对象
        PageRequest pageRequest = PageRequest.of(pageNum, pageSize, sort);

        query.with(pageRequest);


        List<T> tList = findList(query);

        PageImpl<T> page = new PageImpl<>(tList, pageRequest, totalCount);

        return MongoPageVo.<T>builder()
                .pageNum(page.getNumber())
                .pageSize(page.getSize())
                .data(page.getContent())
                .totalPageNum(page.getTotalPages())
                .totalNum(page.getTotalElements())
                .build();
    }

    /**
     * 分页查询
     *
     * @param pageSize 页面大小
     * @param pageNum  页面num
     * @return {@link PageImpl<T>}
     */
    public MongoPageVo<T> findPage(Integer pageSize, Integer pageNum) {
        return findPage(Sort.unsorted(), pageSize, pageNum, new Query());
    }


    /**
     * 总记录条数
     *
     * @return {@link T}
     */
    public Long count() {
        return mongoTemplate.count(new Query(), ReflectUtil.getClassForGeneric(0, this));
    }


    /**
     * 通过ID判断是否存在
     * @param id
     * @return boolean
     */
    public boolean existsById(Serializable id) {
        return !ObjectUtils.isEmpty(findById(id));
    }


    /*
     ******************************** 添加 *************************************************
     */

    /**
     * 添加
     *
     * @param t t
     * @return {@link T}
     */
    public T sava(T t) {
        return mongoTemplate.save(t);
    }


    /**
     * 保存所有
     *
     * @param collection 集合
     * @return {@link List<T>}
     */
    public List<T> saveBatch(Collection<T> collection) {
        return (List<T>) mongoTemplate.insertAll(collection);
    }


    /*
     ******************************** 删除 *************************************************
     */

    /**
     * 删除通过id
     *
     * @param id id
     * @return {@link DeleteResult}
     */
    public DeleteResult removeById(String id) {
        return remove(FIELD_ID_NAME, id);
    }

    /**
     * 通过字段值匹配删除
     *
     * @param filed 提起
     * @param value 价值
     * @return {@link DeleteResult}
     */
    public DeleteResult remove(String filed, String value) {
        return mongoTemplate.remove(new Query().addCriteria(Criteria.where(filed).is(value)), tClass);
    }

    /**
     * 根据集合内容删除
     *
     * @param t t
     * @return {@link DeleteResult}
     */
    public DeleteResult remove(T t) {
        return mongoTemplate.remove(t);
    }

    /**
     * 根据ids进行删除
     *
     * @param ids id
     * @return {@link DeleteResult}
     */
    public DeleteResult removeByIds(List<Serializable> ids) {
        return mongoTemplate.remove(new Query().addCriteria(Criteria.where(FIELD_ID_NAME).in(ids)));
    }

    /*
     ******************************** 修改 *************************************************
     */

    /**
     * 局部修改
     *
     * @param query  查询
     * @param update 更新
     * @return {@link UpdateResult}
     */
    public UpdateResult modify(Query query, Update update) {
        return mongoTemplate.upsert(query, update, tClass);
    }


    /**
     * 修改
     *
     * @param query   查询
     * @param options 选项
     * @return {@link UpdateResult}
     */
    public UpdateResult modify(Query query, Map<String, Object> options) {
        Update update = new Update();
        options.forEach(update::set);
        return modify(query, update);
    }


}
